package com.wawayaya.bookreader.jni;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.FillType;
import android.text.TextUtils;
import android.util.Log;
import com.wawayaya.bookreader.bean.ShortPoint;
import com.wawayaya.bookreader.bean.WeightPoint;
import com.wawayaya.bookreader.bean.WeightPoint1;
import com.wawayaya.bookreader.bean.WritePoint;

import java.io.File;

//import android.graphics.Paint.Style;

/// base class for creating tear-drop shape(line, curve)
public class Brush
{
	/// for log utility
	private static final String TAG = "brush";
	/// the name of .so is libbrush.so
	
	// bypass 2013-01-04 begin: 
	/* libbrush.so, whose modified-time is 2012-4-17 15:03, still supports other devices besides ZiiPad */
	//private static final String s_strName = "brush";
	private static final String s_strName = "cklrd_soft_brush_all";
	// bypass end
	
	/// whether the .so being loaded or not; -1, not trying ever; 0, fail in loading; 1, loaded
	private static int s_nLoad = -1;
	/// the last accessed point(current end point in drawing)
	private static WeightPoint1 mPt = null;
	private static Path m_path;
	private static boolean m_InputChn = true;
	private static boolean  m_Moved = false;
	
	private static int  m_brushid = -1;
	private static BrushFillAgent mbfa = null;
	
	private static Canvas mCanvas;
	//static boolean link = false;  
	
	/*
	public static boolean isZiiSystem() {
		if (System.getProperty("zii.write.key3","").equalsIgnoreCase("20110905hf_ziio_wawayaya_creaf"))
			return true;
		if (System.getProperty("zii.write.key2","").equalsIgnoreCase("20110905hf_ziio_shenbi_creaf"))
			return true;
    	String str;
    	str = System.getProperty("zii.write.key2", "");
    	if (!TextUtils.isEmpty(str)) {
    		if (str.equalsIgnoreCase("20110315hf_ziio_shenbi_creaf"))
    			return true;
    	}
    	str = System.getProperty("zii.vendor.key", "");
    	if (!TextUtils.isEmpty(str)) {
    		if (str.equalsIgnoreCase("Zii Inc."))
    			return true;
    	}
    	
    	str = System.getProperty("ro.zii.platform.id", "");
    	if (!TextUtils.isEmpty(str)) {
    		if (str.equalsIgnoreCase("Zii Platform"))
    			return true;
    	}
    	return false;
    }
    */
	
	public static interface BrushMsg {
		public int OnBrushMsg(float[] fList);
	}
	
	protected static class BrushFillAgent implements BrushMsg {
		protected Canvas mCanvas;
		protected Paint mPaint;
		
		public BrushFillAgent(Canvas canvas, Paint paint) {
			mCanvas = canvas;
			mPaint = paint;
		}
		
		public void close() {
			mCanvas = null;
			mPaint = null;
		}
		
		protected int createFillPathPoint(Path path, float[] fList, int nInit, int nSize) {
			int m, nCount = 0;
			path.reset();
			for(int i=nInit; i<nSize; i++) {
				m = 3 * i;
				float x = fList[m + 0];
				float y = fList[m + 1];
				float f = fList[m + 2];
				int n = (int)(f + 1.001f) - 1;
				switch(n) {
				case 0:
					path.moveTo(x, y);
					break;
				case 1:
					path.lineTo(x, y);
					break;
				case 2:
					if ((i+1 < nSize) && (fList[m + 5] > -0.5f)) {	// >= 0
						path.quadTo(x, y, fList[m + 3], fList[m + 4]);
						i++;
					}
					break;
				default:
					nCount = i + 1 - nInit;
					break;
				}
				if (nCount > 0)
					break;
			}
			path.close();
			if (nCount == 0)
				return nSize - nInit;
			return nCount;
		}

	
		public int OnBrushMsg(float[] fList) {
			// TODO Auto-generated method stub
			if (mCanvas == null || mPaint == null)
				return -1;
			int nSize = (fList!=null ? (fList.length / 3) : 0);
			if (nSize <= 2)
				return -1;
			int nMax = 0;
			mPaint.setAntiAlias(true);
			do{
				Path path = new Path();
				path.setFillType(FillType.WINDING);
				int n = createFillPathPoint(path, fList, nMax, nSize);
				if (n > 0)
					mCanvas.drawPath(path, mPaint);
				else
					break;
				nMax += n;
			}while(nMax < nSize);
			return 0;
		}
	}
	
	public static boolean initialize(String strPath)
	{
		if (s_nLoad >= 0)
			return (s_nLoad != 0);
		try
		{
			boolean bExist = false;
			String strFile = "";
			if (!TextUtils.isEmpty(strPath))
			{
				strFile = strPath + "/lib" + s_strName + ".so";
				File f = new File(strFile);
				bExist = f.exists();
			}
			if (bExist)
				System.load(strFile);
			else
				System.loadLibrary(s_strName);
			//s_nLoad = 1;
		}
		catch(UnsatisfiedLinkError e)
		{
			s_nLoad = 0;
			Log.d(TAG, "" + e.getMessage());
		}
		return (s_nLoad != 0);
    }
	
	/** to initialize, load .so, with predefined loading path; may be called many times
	 * 
	 * @param strPath predefined default path for .so
	 * @return boolean true, if succeed; false, if fail in linking
	 */
	public static boolean initialize(String strPath, Canvas canvas, Paint paint)
	{	
		if (s_nLoad >= 0) {
			
			if (mbfa != null) {
				Brush.disable(m_brushid);
				mbfa.close();
			}
			
			mbfa = new BrushFillAgent(canvas, paint);
			m_brushid = Brush.enable(mbfa);
		}
		
		if (s_nLoad >= 0)
			return (s_nLoad != 0);
		try
		{
			/*
			if(isZiiSystem()) {
				s_strName = "cklrd_soft_brush";
			}
			else{
				s_strName = "cklrd_soft_brush_3rd";
			}
			*/
			boolean bExist = false;
			String strFile = "";
			if (!TextUtils.isEmpty(strPath))
			{
				strFile = strPath + "/lib" + s_strName + ".so";
				File f = new File(strFile);
				bExist = f.exists();
			}
			if (bExist)
				System.load(strFile);
			else
				System.loadLibrary(s_strName);
			s_nLoad = 1;
			m_path = new Path();
			m_path.reset();
			mbfa = new BrushFillAgent(canvas, paint);
			m_brushid = Brush.enable(mbfa);
		}
		catch(UnsatisfiedLinkError e)
		{
			s_nLoad = 0;
			Log.d(TAG, "" + e.getMessage());
		}
		catch(Exception e){
			s_nLoad = 0;
			Log.e("bush1", "brush : initialize : e : " + e);
		}
		catch(Throwable t) {
			s_nLoad = 0;
			Log.e("bush1", "brush : initialize : t : " + t);
		}
		return (s_nLoad != 0);
    }
	
	/////////////////////////////////////////////////////////////////////////////////////
	// native function calls, usually no need to care it
	
	/** to initialize cache for given Canvas, with given maximum width; directly call .so
	 *  
	 * @param canvas GUI object, may not be null(though, in theory, it is not needed)
	 * @param nMaxWeight maximum weight/width, for creating cache
	 * @return int 0, if succeed; -1, if fail
	 */
	public static native int initCanvas(Canvas canvas, int nMaxWeight);
	/** to draw a line, with given parameter
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param xyPos1 LOWORD, x of start point; HIWORD, y of start point
	 * @param xyPos2 LOWORD, x of end point; HIWORD, y of end point
	 * @param nWeight width of line, LOWORD, width of end point; HIWORD, width of start point(may be 0, denotes same as that of end point)
	 * @return int 1, if succeed; 0, if fail
	 */
	public static native int drawLine(Canvas canvas, Paint paint, int xyPos1, int xyPos2, int nWeight);
	/** another form of drawLine, to support float position/width; directly call .so
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param x1 x of start point
	 * @param y1 y of start point
	 * @param x2 x of end point
	 * @param y2 y of end point
	 * @param w1 width of start point
	 * @param w2 width of end point
	 * @return int 1, if succeed; 0, if fail
	 */
	public static native int drawLineEx(Canvas canvas, Paint paint, float x1, float y1, float x2, float y2, float w1, float w2);
	/** to support draw quadratic curve; directly call .so
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param x1 x of start point
	 * @param y1 y of start point
	 * @param x2 x of middle point
	 * @param y2 y of middle point
	 * @param x3 x of end point
	 * @param y3 y of end point
	 * @param w1 width of start point
	 * @param w2 width of middle point
	 * @param w3 width of end point
	 * @return int 1, if succeed; 0, if fail
	 */
	public static native int quadLine(Canvas canvas, Paint paint, float x1, float y1, float x2, float y2, float x3, float y3, float w1, float w2, float w3);
	
	//////////////////////////////////////////////////////////////////////////////////////
	// the latest native API
	public static native int enable( BrushMsg msgCall );
	public static native int disable( int hBrush );
	public static native int move2( int hBrush, float x, float y, float w );
	public static native int line2( int hBrush, float x, float y, float w );
	public static native int quad2( int hBrush, float x1, float y1, float w1, float x2, float y2, float w2 );
	//////////////////////////////////////////////////////////////////////////////////////
	// deprecated function call
	
	/** to encapsulate of drawing line, using WritePoint; is deprecated; please use moveTo/lineTo/quadTo instead
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param wp1 start point
	 * @param wp2 end point
	 * @param nWeight width of line, LOWORD, width of end point; HIWORD, width of start point(may be 0, denotes same as that of end point)
	 * @return int 1, if succeed; 0, if fail
	 */
	public static int drawLine(Canvas canvas, Paint paint, ShortPoint wp1, ShortPoint wp2, int nWeight)
	{
		int n = 0;
		try{
			short x1 = wp1.x;
			short y1 = wp1.y;
			short x2 = wp2.x;
			short y2 = wp2.y;
			int xy1 = x1 + (y1 << 16);
			int xy2 = x2 + (y2 << 16);
			initialize("");
			n = drawLine(canvas, paint, xy1, xy2, nWeight);
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
		return n;
	}
		/** to encapsulate of drawing line, using WritePoint; is deprecated; please use moveTo/lineTo/quadTo instead
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param wp1 start point
	 * @param wp2 end point
	 * @param nWeight width of line, LOWORD, width of end point; HIWORD, width of start point(may be 0, denotes same as that of end point)
	 * @return int 1, if succeed; 0, if fail
	 */
	public static int drawLine(Canvas canvas, Paint paint, WritePoint wp1, WritePoint wp2, int nWeight)
	{
		int n = 0;
		try{
			short x1 = (short)wp1.x;
			short y1 = (short)wp1.y;
			short x2 = (short)wp2.x;
			short y2 = (short)wp2.y;
			int xy1 = x1 + (y1 << 16);
			int xy2 = x2 + (y2 << 16);
			initialize("", canvas, paint);
			n = drawLine(canvas, paint, xy1, xy2, nWeight);
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
		return n;
	}
	
	/////////////////////////////////////////////////////////////////////////////////////
	// the most important function calls
	
	/** change start/current point for drawing line/curve, using WeightPoint
	 * 
	 * @param pt the given point
	 */
	public static void moveTo(WeightPoint pt)
	{
		mPt = new WeightPoint1(pt.mX, pt.mY, pt.mW,1);
		if (s_nLoad < 0)
			initialize("");
	}
		
	/** change start/current point for drawing line/curve, using WeightPoint1
	 * 
	 * @param pt the given point
	 */
	public static void moveTo(Canvas canvas, Paint paint, WeightPoint1 pt)
	{
		mPt = new WeightPoint1(pt.mX, pt.mY, pt.mW,pt.mE);
		//Log.d("PJS", "brush move ("+mPt.mX+","+mPt.mY+","+mPt.mW+")");
		if (!m_InputChn) {
			m_path.reset();
			m_path.moveTo(mPt.mX, mPt.mY);
			m_Moved = false;
		}
		s_nLoad = -1;
		/*if(null == mCanvas || mCanvas != canvas){
			mCanvas = canvas;
			s_nLoad = -1;
		}*/
		
		if (s_nLoad < 0){
			initialize("", canvas, paint);
			s_nLoad = 1;
		}
	}
	/** to encapsulate of drawing line, using WeightPoint
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param pt end point of this line, from current point
	 */
	public static void lineTo(Canvas canvas, Paint paint, WeightPoint pt)
	{
		try{
			if (mPt != null)
				drawLineEx(canvas, paint, mPt.mX, mPt.mY, pt.mX, pt.mY, mPt.mW, pt.mW);
			mPt.mX = pt.mX;
			mPt.mY = pt.mY;
			mPt.mW = pt.mW;
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
	}
		/** to encapsulate of drawing line, using WeightPoint1
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param pt end point of this line, from current point
	 */
	public static void lineTo(Canvas canvas, Paint paint, WeightPoint1 pt)
	{
		try{
			if (mPt != null) {
				if (m_InputChn) {
					//drawLineEx(canvas, paint, mPt.mX, mPt.mY, pt.mX, pt.mY, mPt.mW, pt.mW);
					//BrushFillAgent bfa = new BrushFillAgent(canvas, paint);
					//int nBrushID = brush.enable(bfa);
					//paint.setColor(Color.YELLOW);
					Brush.move2(m_brushid, mPt);
					Brush.line2(m_brushid, pt);
					//brush.disable(nBrushID);
					//bfa.close();
				} else {
					m_path.reset();
					m_path.moveTo(mPt.mX, mPt.mY);
					m_path.lineTo(pt.mX, pt.mY);
					canvas.drawPath(m_path, paint);
					if (!m_Moved) canvas.drawPoint(pt.mX, pt.mY, paint);
				}
			}	
			mPt.mX = pt.mX;
			mPt.mY = pt.mY;
			mPt.mW = pt.mW;
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
	}
	/** to encapsulate drawing quadratic curve; if moveTo not being called; curve start from (0, 0)
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param pt1 the middle point
	 * @param pt2 the end point of this quadratic curve
	 */
	public static void quadTo(Canvas canvas, Paint paint, WeightPoint pt1, WeightPoint pt2)
	{
		try{
			if (mPt == null) {
				mPt = new WeightPoint1();
				if (s_nLoad < 0)
					initialize("");
			}
			quadLine(canvas, paint, mPt.mX, mPt.mY, pt1.mX, pt1.mY, pt2.mX, pt2.mY, mPt.mW, pt1.mW, pt2.mW);
			mPt.mX = pt2.mX;
			mPt.mY = pt2.mY;
			mPt.mW = pt2.mW;
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
	}
		/** to encapsulate drawing quadratic curve; if moveTo not being called; curve start from (0, 0)
	 * 
	 * @param canvas Canvas to be drawn
	 * @param paint Paint to draw
	 * @param pt1 the middle point
	 * @param pt2 the end point of this quadratic curve
	 */
	public static void quadTo(Canvas canvas, Paint paint, WeightPoint1 pt1, WeightPoint1 pt2/*, boolean  real*/)
	{   

		try{
			if (mPt == null) {
				mPt = new WeightPoint1();
				if (s_nLoad < 0)
					initialize("", canvas, paint);
			}
			if (m_InputChn) {
				//quadLine(canvas, paint, mPt.mX, mPt.mY, pt1.mX, pt1.mY, pt2.mX, pt2.mY, mPt.mW, pt1.mW, pt2.mW);
				//BrushFillAgent bfa = new BrushFillAgent(canvas, paint);
				//int nBrushID = brush.enable(bfa);
				/*
				if (real) {
					if (!link) {		
						link = true;
						paint.setColor(Color.BLACK);
						Log.d("PJS", "link == false");
					}
					else {
						link = false;
						paint.setColor(Color.RED);
						Log.d("PJS", "link == true");
					}
				} else {
					paint.setColor(Color.GREEN);
				}*/

				Brush.move2(m_brushid, mPt);
				Brush.quad2(m_brushid, pt1, pt2);
				//brush.disable(nBrushID);
				//bfa.close();
			} else {
				m_path.reset();
				m_path.moveTo(mPt.mX, mPt.mY );
				m_path.quadTo(pt1.mX, pt1.mY, pt2.mX, pt2.mY);
				canvas.drawPath(m_path, paint);
				m_Moved = true;
			}
			
			mPt.mX = pt2.mX;
			mPt.mY = pt2.mY;
			mPt.mW = pt2.mW;
		}catch(UnsatisfiedLinkError e){
			Log.d(TAG, "link:" + e.getMessage());
		}catch(Exception e){
			Log.d(TAG, "error:" + e.getMessage());
		}
	}
	public static void  useBrush(boolean useBrush) {
		m_InputChn = useBrush;
	} 
	public static int move2( int hBrush, WeightPoint1 wp ) {
		return move2(hBrush, wp.mX, wp.mY, wp.mW);
	}
	public static int line2( int hBrush, WeightPoint1 wp ) {
		return line2(hBrush, wp.mX, wp.mY, wp.mW);
	}
	public static int quad2( int hBrush, WeightPoint1 wp1, WeightPoint1 wp2 ) {
		return quad2(hBrush, wp1.mX, wp1.mY, wp1.mW, wp2.mX, wp2.mY, wp2.mW);
	}
}